using System;
using System.Globalization;
using System.Threading;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using GeoFramework.Gps;
#if !PocketPC || DesignTime || Framework20
using System.ComponentModel;
#endif
#if PocketPC
using GeoFramework.Licensing;
#endif

namespace GeoFramework.Controls
{
	/// <summary>
	/// Controls whether controls are rotated to show the current bearing straight up.
	/// </summary>
	public enum RotationOrientation
	{
		/// <summary>
		/// The control will be rotated so that North always points to the top of the screen.
		/// </summary>
		NorthUp = 0,
		/// <summary>
		/// The control will be rotated so the the current bearing points to the top of the screen.
		/// </summary>
		TrackUp = 1
	}
	
	/// <summary>
	/// Represents a user control used to display the location and signal strength of GPS satellites.
	/// </summary>
#if !PocketPC || DesignTime
	[ToolboxBitmap(typeof(SatelliteViewer))]
	[DefaultProperty("Satellites")]
#endif
#if Framework20
#if !PocketPC
    [ToolboxItem(true)]
#endif
    //[DesignTimeVisible(true)]
#endif
	[CLSCompliant(false)]
	public sealed class SatelliteViewer : PolarControl
	{
		private Azimuth pBearing = Azimuth.North;
#if PocketPC
        private Angle pMinorTickInterval = new Angle(5);
#if Framework20
        private Pen pMajorTickPen = new Pen(Color.Black, 2.0f);
#else
		private Pen pMajorTickPen = new Pen(Color.Black);
#endif
#else
		private Angle pMinorTickInterval = new Angle(2);
		private Pen pMajorTickPen = new Pen(Color.Black, 2.0f);
#endif
		private Angle pMajorTickInterval = new Angle(15);
		private Pen pMinorTickPen = new Pen(Color.Black);
		private Angle pDirectionLabelInterval = new Angle(45);
		private string pDirectionLabelFormat = "c";
		private SolidBrush pDirectionLabelBrush = new SolidBrush(Color.Black);
		private Pen pHalfwayUpPen = new Pen(Color.Gray);
#if PocketPC
        private Font pDirectionLabelFont = new Font("Tahoma", 7.0f, FontStyle.Regular);
        private Font pPseudorandomNumberFont = new Font("Tahoma", 7.0f, FontStyle.Bold);
        private SolidBrush pSatelliteFixBrush = new SolidBrush(Color.LimeGreen);
#else
		private Font pDirectionLabelFont = new Font("Tahoma", 12.0f, FontStyle.Bold);
		private Font pPseudorandomNumberFont = new Font("Tahoma", 9.0f, FontStyle.Regular);
#endif
		private SolidBrush pPseudorandomNumberBrush = new SolidBrush(Color.Black);
		private bool pIsUsingRealTimeData = true;
#if !PocketPC
		private SolidBrush pSatelliteShadowBrush = new SolidBrush(Color.FromArgb(32, 0, 0, 0));
		private Color pSatelliteFixColor = Color.LightGreen;
#endif

		private Color pSatelliteNoSignalFillColor = Color.Transparent;
		private Color pSatellitePoorSignalFillColor = Color.Red;
		private Color pSatelliteModerateSignalFillColor = Color.Orange;
		private Color pSatelliteGoodSignalFillColor = Color.Green;
		private Color pSatelliteExcellentSignalFillColor = Color.LightGreen;

		private Color pSatelliteNoSignalOutlineColor = Color.Transparent;
		private Color pSatellitePoorSignalOutlineColor = Color.Black;
		private Color pSatelliteModerateSignalOutlineColor = Color.Black;
		private Color pSatelliteGoodSignalOutlineColor = Color.Black;
		private Color pSatelliteExcellentSignalOutlineColor = Color.Black;

		private ColorInterpolator pFillNone = new ColorInterpolator(Color.Transparent, Color.Red, 10);
		private ColorInterpolator pFillPoor = new ColorInterpolator(Color.Red, Color.Orange, 10);
		private ColorInterpolator pFillModerate = new ColorInterpolator(Color.Orange, Color.Green, 10);
		private ColorInterpolator pFillGood = new ColorInterpolator(Color.Green, Color.LightGreen, 10);
		private ColorInterpolator pFillExcellent = new ColorInterpolator(Color.LightGreen, Color.White, 10);
		private ColorInterpolator pOutlineNone = new ColorInterpolator(Color.Transparent, Color.Gray, 10);
		private ColorInterpolator pOutlinePoor = new ColorInterpolator(Color.Gray, Color.Gray, 10);
		private ColorInterpolator pOutlineModerate = new ColorInterpolator(Color.Gray, Color.Gray, 10);
		private ColorInterpolator pOutlineGood = new ColorInterpolator(Color.Gray, Color.Gray, 10);
		private ColorInterpolator pOutlineExcellent = new ColorInterpolator(Color.Gray, Color.LightGreen, 10);

		private SatelliteCollection pSatellites; // = new SatelliteCollection();
		private RotationOrientation pRotationOrientation = RotationOrientation.TrackUp;

		//private object RenderSyncLock = new object();

		private static PointD[] Icon = new PointD[]
			{
				new PointD(0, 0),
				new PointD(0, 10),
				new PointD(3, 10),
				new PointD(3, 5),
				new PointD(4, 5),
				new PointD(4, 10),
				new PointD(8, 10),
				new PointD(8, 5),
				new PointD(10, 5),
				new PointD(10, 6),
				new PointD(12, 8),
				new PointD(14, 8),
				new PointD(16, 6),
				new PointD(16, 5),
				new PointD(18, 5),
				new PointD(18, 10),
				new PointD(22, 10),
				new PointD(22, 5),
				new PointD(23, 5),
				new PointD(23, 10),
				new PointD(26, 10),
				new PointD(26, 0),
				new PointD(23, 0),
				new PointD(23, 5),
				new PointD(22, 5),
				new PointD(22, 0),
				new PointD(18, 0),
				new PointD(18, 5),
				new PointD(16, 5),
				new PointD(16, 4),
				new PointD(14, 2),
				new PointD(12, 2),
				new PointD(10, 4),
				new PointD(10, 5),
				new PointD(8, 5),
				new PointD(8, 0),
				new PointD(4, 0),
				new PointD(4, 5),
				new PointD(3, 5),
				new PointD(3, 0),
				new PointD(0, 0)
			};
		private static PointD IconCenter = new PointD(13, 5);

        /// <summary>
        /// Creates a new instance.
        /// </summary>
		public SatelliteViewer()
			: base("GeoFramework Multithreaded Satellite Viewer Control (http://www.geoframeworks.com)")
		{
            //MessageBox.Show("SatelliteViewer Initialization started.");

			pSatellites = new SatelliteCollection();
			Orientation = PolarCoordinateOrientation.Clockwise;
			Origin = Azimuth.North;
			// Set the max and min
			CenterR = 0;
			MaximumR = 90;            

#if PocketPC
#if Framework20
            pHalfwayUpPen.DashStyle = DashStyle.Dash;
#endif
#else
			pHalfwayUpPen.DashStyle = DashStyle.DashDotDot;
#endif

#if PocketPC && !Framework20 && !DesignTime
            // Bind global events when GPS data changes
            GeoFramework.IO.Devices.CurrentSatellitesChanged += new SatelliteCollectionEventHandler(Devices_CurrentSatellitesChanged);
            GeoFramework.IO.Devices.CurrentBearingChanged += new AzimuthEventHandler(Devices_CurrentBearingChanged);
#endif
		}

#if !PocketPC || Framework20
        protected override void OnHandleCreated(EventArgs e)
        {
            // Subscribe to events
            try
            {
                base.OnHandleCreated(e);

                // Only hook into events if we're at run-time.  Hooking events
                // at design-time can actually cause errors in the WF Designer.
                if (LicenseManager.CurrentContext.UsageMode == LicenseUsageMode.Runtime)
                {
                    GeoFramework.IO.Devices.CurrentSatellitesChanged += new SatelliteCollectionEventHandler(Devices_CurrentSatellitesChanged);
                    GeoFramework.IO.Devices.CurrentBearingChanged += new AzimuthEventHandler(Devices_CurrentBearingChanged);
                }
            }
            catch
            {
            }
        }

        protected override void OnHandleDestroyed(EventArgs e)
        {
            try
            {
                // Only hook into events if we're at run-time.  Hooking events
                // at design-time can actually cause errors in the WF Designer.
                if (LicenseManager.CurrentContext.UsageMode == LicenseUsageMode.Runtime)
                {
                    GeoFramework.IO.Devices.CurrentSatellitesChanged -= new SatelliteCollectionEventHandler(Devices_CurrentSatellitesChanged);
                    GeoFramework.IO.Devices.CurrentBearingChanged -= new AzimuthEventHandler(Devices_CurrentBearingChanged);
                }
            }
            catch
            {
            }
            finally
            {
                base.OnHandleDestroyed(e);
            }
        }
#endif

		protected override void OnInitialize()
		{

            //MessageBox.Show("SatelliteViewer OnInitialize.");

            base.OnInitialize();

			// Set the collection if it's design mode
            if (LicenseManager.CurrentContext.UsageMode == LicenseUsageMode.Designtime)
            {
                pSatellites = SatelliteCollection.Random(45);
            }
            else
            {
                if (pIsUsingRealTimeData)
                {
                    // Create a new collection if needed
                    if(pSatellites == null)
                        pSatellites = new SatelliteCollection();

                    // Merge it with live satellite data
                    pSatellites.MergeWith(GeoFramework.IO.Devices.CurrentSatellites);
                }
            }

            //MessageBox.Show("SatelliteViewer OnInitialize completed.");

		}



		protected override void Dispose(bool disposing)
		{
#if PocketPC && !Framework20 && !DesignTime

            //MessageBox.Show("SatelliteViewer Dispose.");

			// Bind global events when GPS data changes
			try
			{
				GeoFramework.IO.Devices.CurrentSatellitesChanged -= new SatelliteCollectionEventHandler(Devices_CurrentSatellitesChanged);
			}
			catch
			{
			}
			try
			{
				GeoFramework.IO.Devices.CurrentBearingChanged -= new AzimuthEventHandler(Devices_CurrentBearingChanged);
			}
			catch
			{
			}
#endif

#if PocketPC
			if (pSatelliteFixBrush != null)
			{
				try
				{
					pSatelliteFixBrush.Dispose();
				}
				catch
				{
				}
				finally
				{
					pSatelliteFixBrush = null;
				}
			}
#endif

			if (pMinorTickPen != null)
			{
				try
				{
					pMinorTickPen.Dispose();
				}
				catch
				{
				}
				finally
				{
					pMinorTickPen = null;
				}
			}
			if (pMajorTickPen != null)
			{
				try
				{
					pMajorTickPen.Dispose();
				}
				catch
				{
				}
				finally
				{
					pMajorTickPen = null;
				}
			}
			if (pDirectionLabelBrush != null)
			{
				try
				{
					pDirectionLabelBrush.Dispose();
				}
				catch
				{
				}
				finally
				{
					pDirectionLabelBrush = null;
				}
			}
			if (pDirectionLabelFont != null)
			{
				try
				{
					pDirectionLabelFont.Dispose();
				}
				catch
				{
				}
				finally
				{
					pDirectionLabelFont = null;
				}
			}
#if !PocketPC
			if (pSatelliteShadowBrush != null)
			{
				pSatelliteShadowBrush.Dispose();
				pSatelliteShadowBrush = null;
			}
#endif
			if (pPseudorandomNumberFont != null)
			{
				try
				{
					pPseudorandomNumberFont.Dispose();
				}
				catch
				{
				}
				finally
				{
					pPseudorandomNumberFont = null;
				}
			}
			if (pPseudorandomNumberBrush != null)
			{
				try
				{
					pPseudorandomNumberBrush.Dispose();
				}
				catch
				{
				}
				finally
				{
					pPseudorandomNumberBrush = null;
				}
			}
			if (pHalfwayUpPen != null)
			{
				try
				{
					pHalfwayUpPen.Dispose();
				}
				catch
				{
				}
				finally
				{
					pHalfwayUpPen = null;
				}
			}

			try
			{
				base.Dispose(disposing);
			}
			catch
			{
			}
		}

		

		private Color GetFillColor(SignalToNoiseRatio signal)
		{
			if (signal.Value < 10)
				return pFillNone[signal.Value];
			else if (signal.Value < 20)
				return pFillPoor[signal.Value - 10];
			else if (signal.Value < 30)
				return pFillModerate[signal.Value - 20];
			else if (signal.Value < 40)
				return pFillGood[signal.Value - 30];
			else if (signal.Value < 50)
				return pFillExcellent[signal.Value - 40];
			else
				return pFillExcellent[9];
		}

		private Color GetOutlineColor(SignalToNoiseRatio signal)
		{
			if (signal.Value < 10)
				return pOutlineNone[signal.Value];
			else if (signal.Value < 20)
				return pOutlinePoor[signal.Value - 10];
			else if (signal.Value < 30)
				return pOutlineModerate[signal.Value - 20];
			else if (signal.Value < 40)
				return pOutlineGood[signal.Value - 30];
			else if (signal.Value < 50)
				return pOutlineExcellent[signal.Value - 40];
			else
				return pOutlineExcellent[9];
		}


#if !PocketPC || DesignTime
		[Category("Behavior")]
		[DefaultValue(typeof(Azimuth), "0")]
		[Description("Controls the amount of rotation applied to the entire control to indicate the current direction of travel.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		//[CLSCompliant(false)]
		public Azimuth Bearing
		{
			get
			{
				return pBearing;
			}
			set
			{
				if (pBearing.Equals(value)) return;
				pBearing = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Behavior")]
		[DefaultValue(typeof(bool), "True")]
		[Description("Controls whether the Satellites property is set manually, or automatically read from any available GPS device.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
        public bool IsUsingRealTimeData
		{
			get
			{
				return pIsUsingRealTimeData;
			}
			set
			{
				if (pIsUsingRealTimeData == value) 
                    return;

                //MessageBox.Show("IsUsingRealTimeData started.");


				pIsUsingRealTimeData = value;

#if !DesignTime
				if (pIsUsingRealTimeData)
				{
					// Use current satellite information
					pSatellites.MergeWith(GeoFramework.IO.Devices.CurrentSatellites);
					// Also set the bearing
					if(this.pRotationOrientation == RotationOrientation.TrackUp)
						Rotation = new Angle(-GeoFramework.IO.Devices.CurrentBearing.DecimalDegrees);
				}
				else
				{
					pSatellites.Clear();
					// Clear the rotation
					if(this.pRotationOrientation == RotationOrientation.TrackUp)
						Rotation = Angle.Empty;
				}
#endif
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		#if PocketPC
			[DefaultValue(typeof(Angle), "5")]
		#else
			[DefaultValue(typeof(Angle), "2")]
		#endif
		[Category("Tick Marks")]
		[Description("Controls the number of degrees in between each smaller tick mark around the control.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		//[CLSCompliant(false)]
		public Angle MinorTickInterval
		{
			get
			{
				return pMinorTickInterval;
			}
			set
			{
				if (pMinorTickInterval.Equals(value)) return;
				pMinorTickInterval = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Direction Labels")]
		[DefaultValue(typeof(string), "c")]
		[Description("Controls the format of compass directions drawn around the control.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
        public string DirectionLabelFormat
		{
			get
			{
				return pDirectionLabelFormat;
			}
			set
			{
				if (pDirectionLabelFormat == value) return;
				pDirectionLabelFormat = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[DefaultValue(typeof(Angle), "15")]
		[Category("Tick Marks")]
		[Description("Controls the number of degrees in between each larger tick mark around the control.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		//[CLSCompliant(false)]
        public Angle MajorTickInterval
		{
			get
			{
				return pMajorTickInterval;
			}
			set
			{
				if (pMajorTickInterval.Equals(value)) return;
				pMajorTickInterval = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Tick Marks")]
		[DefaultValue(typeof(Color), "Black")]
		[Description("Controls the color used to draw smaller tick marks around the control.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		public Color MinorTickColor
		{
			get
			{
				return pMinorTickPen.Color;
			}
			set
			{
				lock (pMinorTickPen)
				{
					if (pMinorTickPen.Color.Equals(value)) return;
					pMinorTickPen.Color = value;
				}
				Thread.Sleep(0);
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Tick Marks")]
		[DefaultValue(typeof(Color), "Black")]
		[Description("Controls the color used to draw larger tick marks around the control.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		public Color MajorTickColor
		{
			get
			{
				return pMajorTickPen.Color;
			}
			set
			{
				lock (pMajorTickPen)
				{
					if (pMajorTickPen.Color.Equals(value)) return;
					pMajorTickPen.Color = value;
				}
				Thread.Sleep(0);
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Direction Labels")]
		[DefaultValue(typeof(Angle), "45")]
		[Description("Controls the number of degrees in between each compass label around the control.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		//[CLSCompliant(false)]
		public Angle DirectionLabelInterval
		{
			get
			{
				return pDirectionLabelInterval;
			}
			set
			{
				if (pDirectionLabelInterval.Equals(value)) return;
				pDirectionLabelInterval = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Direction Labels")]
		[DefaultValue(typeof(Color), "Black")]
		[Description("Controls the color used to display compass direction letters around the control.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		public Color DirectionLabelColor
		{
			get
			{
				return pDirectionLabelBrush.Color;
			}
			set
			{
				lock (pDirectionLabelBrush)
				{
					if (pDirectionLabelBrush.Color.Equals(value)) return;
					pDirectionLabelBrush.Color = value;
				}
				Thread.Sleep(0);
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Direction Labels")]
		#if PocketPC
			[DefaultValue(typeof(Font), "Tahoma, 7pt")]
		#else
			[DefaultValue(typeof(Font), "Tahoma, 12pt, style=Bold")]
		#endif
		[Description("Controls the font used to draw compass labels around the control.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		public Font DirectionLabelFont
		{
			get
			{
				return pDirectionLabelFont;
			}
			set
			{
				if (pDirectionLabelFont.Equals(value)) return;
				pDirectionLabelFont = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		[DefaultValue(typeof(Color), "Transparent")]
		[Description("Controls the color inside of satellite icons with no signal.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
        public Color NoSignalFillColor
		{
			get
			{
				return pSatelliteNoSignalFillColor;
			}
			set
			{
				if (pSatelliteNoSignalFillColor.Equals(value)) return;
				pSatelliteNoSignalFillColor = value;
				pFillNone.EndColor = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		[DefaultValue(typeof(Color), "Red")]
		[Description("Controls the color inside of satellite icons with a weak signal.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		public Color PoorSignalFillColor
		{
			get
			{
				return pSatellitePoorSignalFillColor;
			}
			set
			{
				if (pSatellitePoorSignalFillColor.Equals(value)) return;
				pSatellitePoorSignalFillColor = value;
				pFillNone.EndColor = value;
				pFillPoor.StartColor = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		[DefaultValue(typeof(Color), "Orange")]
		[Description("Controls the color inside of satellite icons with a moderate signal.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		public Color ModerateSignalFillColor
		{
			get
			{
				return pSatelliteModerateSignalFillColor;
			}
			set
			{
				if (pSatelliteModerateSignalFillColor.Equals(value)) return;
				pSatelliteModerateSignalFillColor = value;
				pFillModerate.StartColor = value;
				pFillPoor.EndColor = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		[DefaultValue(typeof(Color), "Green")]
		[Description("Controls the color inside of satellite icons with a strong signal.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
        public Color GoodSignalFillColor
		{
			get
			{
				return pSatelliteGoodSignalFillColor;
			}
			set
			{
				if (pSatelliteGoodSignalFillColor.Equals(value)) return;
				pSatelliteGoodSignalFillColor = value;
				pFillGood.StartColor = value;
				pFillModerate.EndColor = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		[DefaultValue(typeof(Color), "LightGreen")]
		[Description("Controls the color inside of satellite icons with a very strong signal.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
        public Color ExcellentSignalFillColor
		{
			get
			{
				return pSatelliteExcellentSignalFillColor;
			}
			set
			{
				if (pSatelliteExcellentSignalFillColor.Equals(value)) return;
				pSatelliteExcellentSignalFillColor = value;
				pFillGood.EndColor = value;
				pFillExcellent.StartColor = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		[DefaultValue(typeof(Color), "Transparent")]
		[Description("Controls the color around satellite icons with no signal.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		public Color NoSignalOutlineColor
		{
			get
			{
				return pSatelliteNoSignalOutlineColor;
			}
			set
			{
				if (pSatelliteNoSignalOutlineColor.Equals(value)) return;
				pSatelliteNoSignalOutlineColor = value;
				pOutlineNone.EndColor = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		[DefaultValue(typeof(Color), "Black")]
		[Description("Controls the color around satellite icons with a weak signal.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		public Color PoorSignalOutlineColor
		{
			get
			{
				return pSatellitePoorSignalOutlineColor;
			}
			set
			{
				if (pSatellitePoorSignalOutlineColor.Equals(value)) return;
				pSatellitePoorSignalOutlineColor = value;
				pOutlineNone.EndColor = value;
				pOutlinePoor.StartColor = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		[DefaultValue(typeof(Color), "Black")]
		[Description("Controls the color around satellite icons with a moderate signal.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
        public Color ModerateSignalOutlineColor
		{
			get
			{
				return pSatelliteModerateSignalOutlineColor;
			}
			set
			{
				if (pSatelliteModerateSignalOutlineColor.Equals(value)) return;
				pSatelliteModerateSignalOutlineColor = value;
				pOutlinePoor.EndColor = value;
				pOutlineModerate.StartColor = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		[DefaultValue(typeof(Color), "Black")]
		[Description("Controls the color around satellite icons with a strong signal.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
        public Color GoodSignalOutlineColor
		{
			get
			{
				return pSatelliteGoodSignalOutlineColor;
			}
			set
			{
				if (pSatelliteGoodSignalOutlineColor.Equals(value)) return;
				pSatelliteGoodSignalOutlineColor = value;
				pOutlineModerate.EndColor = value;
				pOutlineGood.StartColor = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		[DefaultValue(typeof(Color), "Black")]
		[Description("Controls the color around satellite icons with a very strong signal.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
        public Color ExcellentSignalOutlineColor
		{
			get
			{
				return pSatelliteExcellentSignalOutlineColor;
			}
			set
			{
				if (pSatelliteExcellentSignalOutlineColor.Equals(value)) return;
				pSatelliteExcellentSignalOutlineColor = value;
				pOutlineGood.EndColor = value;
				pOutlineExcellent.StartColor = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		[DefaultValue(typeof(Color), "LimeGreen")]
		[Description("Controls the color of the ellipse drawn around fixed satellites.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		public Color FixColor
		{
			get
			{
#if PocketPC
				return pSatelliteFixBrush.Color;
#else
				return pSatelliteFixColor;
#endif
			}
			set
			{
#if PocketPC
				if(pSatelliteFixBrush.Color.Equals(value))
					return;
				pSatelliteFixBrush.Color = value;
#else
				if (pSatelliteFixColor.Equals(value)) return;
				pSatelliteFixColor = value;
#endif
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Behavior")]
		[DefaultValue(typeof(RotationOrientation), "TrackUp")]
		[Description("Controls which bearing points straight up on the screen.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
        public RotationOrientation RotationOrientation
		{
			get
			{
				return pRotationOrientation;
			}
			set
			{
				if(pRotationOrientation == value)
					return;
				pRotationOrientation = value;

				// If this becomes active, set the current bearing
				if(pRotationOrientation == RotationOrientation.TrackUp)
					Rotation = new Angle(-GeoFramework.IO.Devices.CurrentBearing.DecimalDegrees);
				else
					Rotation = Angle.Empty;

				//InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellites")]
		[Description("Contains the list of satellites drawn inside of the control.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		//[CLSCompliant(false)]
		public SatelliteCollection Satellites
		{
			get
			{
				return pSatellites;
			}
			set
			{
				// Detatch old events
				//                if (pSatellites != null)
				//                {
				//                    pSatellites.IsEventsEnabled = false;
				//                    pSatellites.AzimuthChanged -= new SatelliteEventHandler(SatelliteChanged);
				//                    pSatellites.ElevationChanged -= new SatelliteEventHandler(SatelliteChanged);
				//                    pSatellites.Fixed -= new SatelliteEventHandler(SatelliteChanged);
				//                    pSatellites.FixLost -= new SatelliteEventHandler(SatelliteChanged);
				//                    pSatellites.SignalStrengthChanged -= new SatelliteEventHandler(SatelliteChanged);
				//                }
                //MessageBox.Show("Satellites started.");

				lock (this)
				{
					if(value == null)
						pSatellites.Clear();
					else
						pSatellites = value;
				}
				Thread.Sleep(0);

				if (pSatellites != null)
				{
					//                    pSatellites.IsEventsEnabled = true;
					//                    pSatellites.AzimuthChanged += new SatelliteEventHandler(SatelliteChanged);
					//                    pSatellites.ElevationChanged += new SatelliteEventHandler(SatelliteChanged);
					//                    pSatellites.Fixed += new SatelliteEventHandler(SatelliteChanged);
					//                    pSatellites.FixLost += new SatelliteEventHandler(SatelliteChanged);
					//                    pSatellites.SignalStrengthChanged += new SatelliteEventHandler(SatelliteChanged);
					// Re-render
					InvokePaintOffScreen();
				}

                //MessageBox.Show("Satellites complete.");

			}
		}

#if Framework20 && !PocketPC
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public override Azimuth Origin
        {
            get
            {
                return base.Origin;
            }
            set
            {
                base.Origin = value;
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public override Angle Rotation
        {
            get
            {
                return base.Rotation;
            }
            set
            {
                base.Rotation = value;
            }
        }
#endif
		//[CLSCompliant(false)]
		protected override void OnPaintOffScreen(CancelablePaintEventArgs e)
		{
            //MessageBox.Show("SatelliteViewer painting.");

			PolarGraphics f = CreatePolarGraphics(e.Graphics);
			
			// Cache drawing intervals and such to prevent a race condition
            double MinorInterval = pMinorTickInterval.DecimalDegrees;
            double MajorInterval = pMajorTickInterval.DecimalDegrees;
            double DirectionInterval = pDirectionLabelInterval.DecimalDegrees;

			// Draw tick marks
            if (MinorInterval > 0)
            {
                for (double angle = 0; angle < 360; angle += MinorInterval)
                {
                    // And draw a line
                    f.DrawLine(pMinorTickPen, new PolarCoordinate(88.0f, angle),
                        new PolarCoordinate(90, angle));
                }
            }

			// Draw tick marks
            if (MajorInterval > 0)
			{
                for (double angle = 0; angle < 360; angle += MajorInterval)
				{
					// And draw a line
					f.DrawLine(pMajorTickPen, new PolarCoordinate(85.0f, angle), new PolarCoordinate(90, angle));
				}
			}			

            if (DirectionInterval > 0)
			{
                for (double angle = 0; angle < 360; angle += DirectionInterval)
				{
					// Get the coordinate of the line's start
					PolarCoordinate start = new PolarCoordinate(70, angle, Azimuth.North, PolarCoordinateOrientation.Clockwise);
#if PocketPC
					f.DrawCenteredString(((Azimuth)angle).ToString(pDirectionLabelFormat, CultureInfo.CurrentCulture), pDirectionLabelFont, pDirectionLabelBrush, start);
#else
                    f.DrawRotatedString(((Azimuth)angle).ToString(pDirectionLabelFormat, CultureInfo.CurrentCulture), pDirectionLabelFont, pDirectionLabelBrush, start);
#endif
				}
			}

			// Draw an ellipse at the center
			f.DrawEllipse(pHalfwayUpPen, PolarCoordinate.Empty, 45);

			// Now draw each satellite
			lock (pSatellites.SyncRoot)
			{

#if !PocketPC

					foreach (Satellite satellite in pSatellites)
					{
						// Don't draw if the satellite is stale
						if (!satellite.IsActive && !DesignMode)
							continue;

						// Is the satellite transparent?
						if (GetFillColor(satellite.SignalToNoiseRatio).A < pSatelliteShadowBrush.Color.A)
							continue;

						// Get the coordinate for this satellite
						PolarCoordinate Center = new PolarCoordinate(Convert.ToSingle(90.0f - satellite.Elevation.DecimalDegrees),
							satellite.Azimuth.DecimalDegrees, Azimuth.North, PolarCoordinateOrientation.Clockwise);
						PointD CenterPoint = f.ToPointD(Center);

						// Each icon is 30x30, so we'll translate it by half the distance
						double pShadowSize = Math.Sin(Radian.FromDegrees(satellite.Elevation.DecimalDegrees).Value) * 7;

						f.Graphics.TranslateTransform((float)(CenterPoint.X - IconCenter.X + pShadowSize), (float)(CenterPoint.Y - IconCenter.Y + pShadowSize));

						// Draw each satellite
						PointF[] SatelliteIcon = new PointF[Icon.Length];
						for (int index = 0; index < Icon.Length; index++)
						{
							SatelliteIcon[index] = (PointF)Icon[index];
						}

						using (Matrix y = new Matrix())
						{
							y.RotateAt(Convert.ToSingle(satellite.Azimuth.DecimalDegrees - f.Rotation.DecimalDegrees + Origin.DecimalDegrees), 
                                (PointF)IconCenter, MatrixOrder.Append);
							y.TransformPoints(SatelliteIcon);
						}

						f.Graphics.FillPolygon(pSatelliteShadowBrush, SatelliteIcon);
						f.Graphics.ResetTransform();
					}
			
#endif			

				// Now draw each satellite				
				foreach (Satellite satellite in pSatellites)
				{
                    if (LicenseManager.CurrentContext.UsageMode != LicenseUsageMode.Designtime)
                    {
#if PocketPC


                        // Don't draw if the satellite is stale
                        if (!satellite.IsActive)
                            continue;
#else
						// Don't draw if the satellite is stale
						if (!satellite.IsActive && !DesignMode)
							continue;
#endif
                    }

					// Each icon is 30x30, so we'll translate it by half the distance
					if (satellite.IsFixed)
					{
						// Get the coordinate for this satellite
						PolarCoordinate Center = new PolarCoordinate(Convert.ToSingle(90.0 - satellite.Elevation.DecimalDegrees),
							satellite.Azimuth.DecimalDegrees, Azimuth.North, PolarCoordinateOrientation.Clockwise);

#if PocketPC
						f.FillEllipse(pSatelliteFixBrush, Center, 16);
#else
							using (SolidBrush FixBrush = new SolidBrush(Color.FromArgb(Math.Min(255, pSatellites.FixedSatellites.Count * 20), pSatelliteFixColor)))
							{
								f.FillEllipse(FixBrush, Center, 16);
							}
#endif

					}

				}


				// Now draw each satellite
				foreach (Satellite satellite in pSatellites)
				{
                    if (LicenseManager.CurrentContext.UsageMode != LicenseUsageMode.Designtime)
                    {
#if PocketPC
                        // Don't draw if the satellite is stale
                        if (!satellite.IsActive)
                            continue;
#else
						// Don't draw if the satellite is stale
						if (!satellite.IsActive && !DesignMode)
							continue;
#endif
                    }

					// Get the coordinate for this satellite
					PolarCoordinate Center = new PolarCoordinate(Convert.ToSingle(90.0 - satellite.Elevation.DecimalDegrees),
						satellite.Azimuth.DecimalDegrees, Azimuth.North, PolarCoordinateOrientation.Clockwise);
					PointD CenterPoint = f.ToPointD(Center);

#if PocketPC
					// Manually rotate each point of the icon
					Point[] SatelliteIcon = new Point[Icon.Length];
					for (int index = 0; index < Icon.Length; index++)
					{
						SatelliteIcon[index] = (Point)Icon[index]
							.RotateAt(satellite.Azimuth.DecimalDegrees - Rotation.DecimalDegrees + Origin.DecimalDegrees, IconCenter)
							.Add(CenterPoint.X - IconCenter.X, CenterPoint.Y - IconCenter.Y);
					}

					Color SatelliteColor = GetFillColor(satellite.SignalToNoiseRatio);
					SolidBrush FillBrush = new SolidBrush(SatelliteColor);
					f.Graphics.FillPolygon(FillBrush, SatelliteIcon);
					FillBrush.Dispose();

#else
						// Each icon is 30x30, so we'll translate it by half the distance
						double pShadowSize = Math.Sin(Radian.FromDegrees(satellite.Elevation.DecimalDegrees).Value) * 7;

						f.Graphics.TranslateTransform((float)(CenterPoint.X - IconCenter.X - pShadowSize * 0.1), (float)(CenterPoint.Y - IconCenter.Y - pShadowSize * 0.1));

						// Draw each satellite
						PointF[] SatelliteIcon = new PointF[Icon.Length];
						for (int index = 0; index < Icon.Length; index++)
						{
							SatelliteIcon[index] = (PointF)Icon[index];
						}

						Matrix y = new Matrix();
						y.RotateAt(Convert.ToSingle(satellite.Azimuth.DecimalDegrees - f.Rotation.DecimalDegrees + Origin.DecimalDegrees), (PointF)IconCenter, MatrixOrder.Append);
						y.TransformPoints(SatelliteIcon);
						y.Dispose();

						SolidBrush FillBrush = new SolidBrush(GetFillColor(satellite.SignalToNoiseRatio));
						f.Graphics.FillPolygon(FillBrush, SatelliteIcon);
						FillBrush.Dispose();

						Pen FillPen = new Pen(GetOutlineColor(satellite.SignalToNoiseRatio), 1.0f);
						f.Graphics.DrawPolygon(FillPen, SatelliteIcon);
						FillPen.Dispose();

						f.Graphics.ResetTransform();
#endif

#if PocketPC
					if (!SatelliteColor.Equals(Color.Transparent))
					{
						f.DrawCenteredString(satellite.PseudorandomNumber.ToString(), pPseudorandomNumberFont,
							pPseudorandomNumberBrush, new PolarCoordinate(Center.R - 11, Center.Theta.DecimalDegrees,
							Azimuth.North, PolarCoordinateOrientation.Clockwise));
					}
#else
                    
						f.DrawRotatedString(satellite.PseudorandomNumber.ToString(CultureInfo.CurrentCulture), pPseudorandomNumberFont,
							pPseudorandomNumberBrush, new PolarCoordinate((float)(Center.R - 11), Center.Theta,
							Azimuth.North, PolarCoordinateOrientation.Clockwise));
#endif
			
				}	
			}
			Thread.Sleep(0);

            //MessageBox.Show("SatelliteViewer painting complete.");

		}

#if !PocketPC
		[Category("Appearance")]
		[DefaultValue(typeof(Color), "32, 0, 0, 0")]
		[Description("Controls the color of the shadow cast by satellite icons.")]
		public Color ShadowColor
		{
			get
			{
				return pSatelliteShadowBrush.Color;
			}
			set
			{
				lock (pSatelliteShadowBrush)
				{
					if (pSatelliteShadowBrush.Color.Equals(value)) return;
					pSatelliteShadowBrush.Color = value;
				}
				InvokePaintOffScreen();
			}
		}
#endif

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		#if PocketPC
			[DefaultValue(typeof(Font), "Tahoma, 7pt, style=Bold")]
		#else
			[DefaultValue(typeof(Font), "Tahoma, 9pt")]
		#endif
		[Description("Controls the font used to display the ID of each satellite.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
		public Font PseudorandomNumberFont
		{
			get
			{
				return pPseudorandomNumberFont;
			}
			set
			{
				if (pPseudorandomNumberFont.Equals(value)) return;
				pPseudorandomNumberFont = value;
				InvokePaintOffScreen();
			}
		}

#if !PocketPC || DesignTime
		[Category("Satellite Colors")]
		[DefaultValue(typeof(Color), "Black")]
		[Description("Controls the color used to display the ID of each satellite.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(true)]
		[EditorBrowsable(EditorBrowsableState.Always)]
#endif
        public Color PseudorandomNumberColor
		{
			get
			{
				return pPseudorandomNumberBrush.Color;
			}
			set
			{
				lock (pPseudorandomNumberBrush)
				{
					if (pPseudorandomNumberBrush.Color.Equals(value)) return;
					pPseudorandomNumberBrush.Color = value;
				}
				Thread.Sleep(0);
				InvokePaintOffScreen();
			}
		}

		private void SatelliteChanged(object sender, SatelliteEventArgs e)
		{
            //MessageBox.Show("SatelliteChanged.");

			InvokePaintOffScreen();
		}

		private void Devices_CurrentSatellitesChanged(object sender, SatelliteCollectionEventArgs e)
		{
            //MessageBox.Show("CurrentSatellitesChanged.");

#if !DesignTime
			if (pIsUsingRealTimeData)
			{
				lock(pSatellites.SyncRoot)
				{
					if(pSatellites.MergeWith(e.SatelliteCollection))
						BeginInvokePaintOffScreen();
				}
			}
#endif
		}

		private void Devices_CurrentBearingChanged(object sender, AzimuthEventArgs e)
		{
            //MessageBox.Show("CurrentBearingChanged.");

			if (pIsUsingRealTimeData && pRotationOrientation == RotationOrientation.TrackUp)
				Rotation = new Angle(e.Azimuth.DecimalDegrees);
		}
	}
}
